﻿#include<stdio.h>

/*
算数操作符：+ - * % /
移位操作符：<<  左移操作符      >>右移操作符
//
算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员

*/


/*
算术操作符
1. / 整型的除法1/2==0    浮点型的除法1.0/2==0.5  

2. % 取余数，两端必须是整数
*/






/*

移位操作符：移动的是二进制位

<< 左移操作符
>> 右移操作符
    
注：1. 移位操作符的操作数只能是整数！！！
    2. 移动的位置数只能是整数！不能移动负数位
16进制：0-9 a-f

二进制：
整数的二进制表示有三种:原码 反码 补码

正整数的原码、反码、补码相同
7 的二进制序列是111。7四个字节，32个比特位。
第一个0是最高位，7是正数，最高位是0。最高位是1表示为负数     ，0表示正数
00000000000000000000000000000111  --  7的原码，反码，补码 
负整数的原码、反码、补码需要计算
-7
10000000000000000000000000000111  --  -7的原码      （1是符号位，表示负数） 
11111111111111111111111111111000  --  -7的反码      （原码的符号位不变，其他位取反）
11111111111111111111111111111001  --  -7的补码      （反码的二进制序列+1，得到补码）
所以，负数的原码，补码，反码，都能直接看出是负数
第一位0，无论哪一种码，都是正的；第一位是1，都是负的。

整数在内存中存储的是补码


左移操作符的魅力：
左移1位，原数字*2
Cn=C（n-1）*2   
左移两位的结果是左移1位再*2；
左移n位的结果是左移n-1位的结果再*2

*/

/*
左移n位：补码左边丢失n位，空缺的右边补充0； 
*/
//#include<stdio.h>
//int main()
//{
//	int a = 7;
//	int b = a << 1;            //箭头朝左，向左移 1 位，移动的是存到内存中的补码
//	printf("a=%d\n", a);
//	printf("b=%d\n", b);
//	return 0;
//}


//#include<stdio.h>
//int main()
//{
//	int a = -7;
//	int b = a << 3;            //箭头朝左，向左移 1 位，移动的是存到内存中的补码，补码补充0
//	//但是村下的是补码，补码-1得到反码；反码符号位不变，其他位置取反得到原码。我们看到是原码。算完原码再取负数。
//	//（二进制10-1=01，类比10进制10-1，10借1，个位+10（加上进制），取0，得到09，只写9）
//	printf("a=%d\n", a);
//	printf("b=%d\n", b);
//	return 0;
//}


//左移左边丢弃，右移右边丢弃



/*
右移操作符：1.算数位移 2.逻辑位移

VS编译器采用的是算数移位，所以符号不变。
什么移位方式取决于编译器！

1.算数移位n位：右边丢弃n位，左边补原符号位n位（只是单纯算数，符号不变）

2.逻辑移位：右边丢弃n位，左边补n个0（逻辑反而没有逻辑，直接补0）。结果全是正的

比较小的数字最左边的符号位只决定正负！
//所以整数的逻辑移位和算术移位都是补0.没有影响。

1111111111111111111111111111111000-1=1111111111111111111111111111110111
二进制加减法：
加法：0+0=0；0+1=1；1+0=1；1+1=10；0进位为1。
减法：0－0=0，1－0=1，1－1=0，0－1=1。减法需要注意的是，0-1=1！（可以理解为没有负数，所以取消负号）其余的可以按照十进制原酸
运算减法时：111111000-1，找到最后一个1，位置位1000，所以用1000的每一位-1，1-1=0，0-0=1，0-0=1，0-0=1。所以结果是0111


最后补码到原码，千万别忘了最前面的符号位！！！
*/


//#include<stdio.h>
//int main()
//{
//	int a = -7;
//	int b = a >> 1;     //右移1位：最右边1位丢失，
//	printf("a=%d\n", a);
//	printf("b=%d\n", b);
//	return 0;
//}





/*

位（2进制位）操作符：

& //按位（2进制位）与
| //按位（2进制位）或
^ //按位（2进制位） 异或

注：1.他们的操作数必须是整数。
    
*/

/*
按位与&：
1.当二进制补码每对对应位置全是1，得到的结果统一成1；只要每对有一个是0，每对统一成0.
2.计算完补码之后，转换为原码显示（补码只是存储形式）

&按位与，正的0
*/

//int main()
//{
//	int a = 3;
//	int b = -5;
//	int c = a & b;
//	//00000000000000000000000000000011 - 3的补码
//	//10000000000000000000000000000101
//	//11111111111111111111111111111010
//	//11111111111111111111111111111011 - -5的补码
//	//00000000000000000000000000000011 - 3的补码
//	//00000000000000000000000000000011
//	//
//	//%d 意味着打印一个有符号的整数
//	//
//	printf("c=%d\n", c);
//
//	return 0;
//}



/*
按位或 |  ：

对应的二进制位，只要有 1 ，则为 1；
同时为 0 ，才为 0 
按位或，负的-1
*/

//int main()
//{
//    int a = 3;
//    int b = -5;
//    int c = a | b;
////	//00000000000000000000000000000011 - 3的补码
////	//10000000000000000000000000000101
////	//11111111111111111111111111111010
////	//11111111111111111111111111111011 - -5的补码
////	//00000000000000000000000000000011 - 3的补码
//
//
//    //c=-5;
//    return 0;
// 
//}






/*
按位异或   ^

相同为0，相异为1.（相同是正数）

特点：
两个相同的数字按位异或==0 3^3=0   a^a==0;
0^a==a   0与其他整数a按位异或==a;

∴3^3^5==0^5==5
3^5^3==5(按位异或满足交换律）3^3^5=3^5^3
*/


//



//
//int main()
//{
//	int a = 3;
//	int b = 5;
//
//	printf("交换前：a=%d b=%d\n", a, b);
//	a = a ^ b;//a=3^5
//	b = a ^ b;//3^5^5 --> b=3
//	a = a ^ b;//3^5^3 --> a=5
//
//	printf("交换后：a=%d b=%d\n", a, b);
//
//	return 0;
//}






//编写代码实现：求一个整数存储在内存中的二进制中1的个数。
//即求补码中1的个数



/*

5.赋值操作符

赋值操作符可以连续使用，比如：
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值      把y+1赋给x，再把x赋给a


复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=



*/


//int main()
//{
//
//    int a = 3;
//    a += 5;  //a=a+5
//    a = a >> 1;//a>>=1;
//    return 0;
//
//}





/*


单目操作符：

!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度（以字节为单位）
~           对一个数的二进制按位取反（取完反是补码（一定都是对补码进行操作），需要计算回原码）
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符)
(类型)       强制类型转换




*/





// int main()
//{
//	int flag ；
//	//flag为真,进入if
//	if (flag)
//	{}
//	
//	//flag为假,进入if
//	if(!flag)
//	{}
//
//	return 0;
//}

//
//C语言中0表示假，非0表示真
//



//int main()
//{
//	//int a = -10;
//	//int b = -a;
//	//printf("%d\n", a); 
//	//printf("%d\n", b);
//
//	int a = 10;
//	printf("%p\n",  &a);
//	int* p = &a;//p就是指针变量
//
//	return 0;
//}
//
// 
// 
// 
//int main()
//{
//	int arr[5] = {0};
//	printf("%d\n", sizeof(arr));
//
//	//int a = 10;
//	////int n = sizeof(a);//计算的是a所占内存的大小，单位是字节
//	//int n = sizeof(int);
//	////sizeof是一个操作符
//	////计算的是变量所占内存空间的大小,单位是字节
//	////计算类型所创建的变量占据空间的大小,单位是字节
//	////
//	//printf("n=%d\n", n);
//
//	return 0;
//}
//




//int main()
//{
//	//int a = 0;
//	////~ 是按二进制位取反
//	////00000000000000000000000000000000 - 补码
//	////11111111111111111111111111111111 -> ~a
//	////11111111111111111111111111111110
//	////10000000000000000000000000000001
//	////-1
//	//printf("%d\n", ~a);//-1
// 
// 
////	//int a = 3;
//	////00000000000000000000000000000011
//	////11111111111111111111111111111100 - 补码
//	////11111111111111111111111111111011
//	////10000000000000000000000000000100
//	////-4
//	//printf("%d\n", ~a);
////	int a = 13;
//	a |= (1 << 4);
//	printf("%d\n", a);//a=29
//	a &= (~(1 << 4));
//	printf("%d\n", a);//a=13
//
//	//00000000000000000000000000011101
//	//11111111111111111111111111101111
//	//00000000000000000000000000001101
//
//	//11111111111111111111111111101111
//	//00000000000000000000000000010000   //按位取反得到上边的数字
//	//
//	//00000000000000000000000000001101
//	//00000000000000000000000000011101
//	//00000000000000000000000000000010
//	//1<<1    //把1向左移动n-1位，再按位异或 |（&按位与  |按位或  ^按位异或）
//	//00000000000000000000000000001111
//	//
// 负数也遵循按位与 按位异或 按位或 的特点（都是利用二进制操作）
//	return 0;
//}
//


//void test(int n)
//{}
//int main()
//{
//	//int a = 3;
//	////int b = ++a;//前置++，先++，后使用
//	////a=a+1,b=a;
//	//int b = a++;//后置++，先使用，再++
//	////b=a, a=a+1
//	//printf("%d\n", a);//
//	//printf("%d\n", b);//
//
//	//int a = 3;
//	//int b = a--;
//	////b=a,a=a-1
//	////int b = --a;//前置--，先--，后使用
//	////a=a-1,b=a
//	//printf("%d\n", a);//2
//	//printf("%d\n", b);//3
//
//	//int a = 10;
//	//printf("%d\n", a--);//?
//	//printf("%d\n", a);//?
//
//	int a = 10;
//	test(a--);
//
//	return 0;
//}



//int main()
//{
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//
//	}
//	for (i = 0; i < 10; ++i)
//	{
//
//	}
//
//	return 0;
//}
//
//int main()
//{
//	int a = 10;
//	int* p = &a;
//	*p = 20;
//	printf("%d\n", a);
//
//	return 0;
//}
//



//int main()
//{
//	//time_t;//long long
//	srand((unsigned int)time(NULL));
//	int a = (int)3.14;
//	printf("%d\n", a);
//	return 0;
//}


//int main()
//{
//	int a = 0;
//	printf("%d\n", sizeof(a));
//	printf("%d\n", sizeof(int));
//
//	printf("%d\n", sizeof a);//ok
//
// 
// 数组传参，别看他人模狗样传数组，实际上直传首字符的！！！指针（比如arr，ch……）！！！      别管是什么类型的指针，都是四个或者八个字节！！！
//	//printf("%d\n", sizeof int);//err
//
//	return 0;
//}

//sizeof是操作符，不是函数
//strlen是库函数，是用来求字符串长度（只能字符串）




//关系操作符
/*
>
>=
<
<=
!=   用于测试“不相等”
==      用于测试“相等”

在编程的过程中== 和=不小心写错，导致的错误
*/

//int main()
//{
//	if (3 == 5)
//	{
//	}
//	//err
//	if ("abc" == "abcdef")//这样写是在比较2个字符串的首字符的地址
//	{
//
//	}
//  //两个字符串比较相等应该使用strcmp
//	return 0;
//}

/*

逻辑操作符
*/


//
//int main()
//{
//	int a = 0;
//	int b = 0;
//	//int c = a && b;//并且          a!=0,b!=0,打印后，c==1(a&&b为真，1为真）      。只要a，b中一个为0，那么c为假，打印为0
//
//	int c = a || b;  //或者       只要一个为真，那就是真（打印真，打印为1）
//
//	printf("%d\n", c);
//
//	//if (a && b)
//	//{
//
//	//}
//	return 0;
//}



// 1. 能被4整除，并且不能被100整除
//2. 能被400整除是闰年
//
//int is_leap_year(int y)
//{
//	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
//	{
//		return 1;
//	}
//	else
//	{
//		return 0;
//	}
//}
//
//int main()
//{
//	return 0;
//}

//
// 总结：
// 
//逻辑与 && 左边为假，右边就不计算了（只要有一个为假，整体为假，不进行后续计算）
//逻辑或 || 左边为真，右边就不计算了（只要有一个为真，整体为真，不进行后续计算）
//
//#include <stdio.h>
//int main()
//{
//	int i = 0, a = 0, b = 2, c = 3, d = 4;
//	//i = a++ && ++b && d++;         
// /*
// 此处i=后边一串，先算后边一串，再赋值给a，计算a++时，类比b=a++,先使用b=0，后++，所以a++在此处先使用，为0
// 既然a=0，那后边的不用算了，假&&任何东西，都是假，所以此处i=0;
// 在计算时，a++,a在此处使用0，使整体表达式立刻判定为假，假，所以后边不进行计算，但是a++还要计算，所以打印a==1，后面的不计算，数值不变
//（先使用，后++，中的先使用是指，在此表达式中使用）
// 打印时a=1,b=2,c=3,d=4
// 
// 
// //a=1（后续运算继续进行）
////打印为 2 3 3 5
// 
// */
// 
// 
// a=1 ， b = 2, c = 3, d = 4;
//	i = a++ || ++b || d++;
//	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
//打印结果为2 2 3 4
// /*
// 在计算时，！表达式中！，a==1，或运算符，只要有一个为真，后面的就不进行运算了。所以，运算后，a==2,b==2,c==3,d==4
// 
// */
// 
// 
// i=0, a=0 ， b = 2, c = 3, d = 4;
//	i = a++ || ++b || d++;
//	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
// 
// 打印结果是 1 3 3 4
// /*
// 在表达式中，a使用的数值是0，由于是 或运算 ，所以继续运算，b在表达式中的使用值为 3 （先++，后使用）,为真
// 所以，表达式为真,不进行后续的运算。所以a==1,b==3,c==3,d==4
// 
// */
//	return 0;
//}
//





/*
条件操作符：三目操作符
exp1 ? exp2 : exp3
真      算   不算
假     不算  算
算的结果是整个表达式的结果
*/


//int main()
//{
//	int a = 3;
//	int b = 0;
//	
//	int max = (a > b ? a : b);
//
//	if (a > 5)
//		b = 3;
//	else
//		b = -3;
//	
//	(a > 5) ? (b = 3) : (b = -3);
//
//	b = (a > 5 ? 3 : -3);
//
//	return 0;
//}


/*
逗号表达式：
exp1, exp2, exp3, …expN
逗号表达式，就是用逗号隔开的多个表达式。
逗号表达式，从左向右依次执行。整个表达式的结果是最后一个表达式的结果。



//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是13    


//代码2
if (a =b + 1, c=a / 2, d > 0)   （虽然会计算前面的数据，但是只拿d进行判断）



//代码3
a = get_val();
count_val(a);
while (a > 0)
{
        //业务处理
        a = get_val();
        count_val(a);
}

如果使用逗号表达式，改写：
while (a = get_val(), count_val(a), a>0)        //先算完前两步，再判断a
{
         //业务处理
}

*/




/*

11. 下标引用、函数调用和结构成员

1. [ ] 下标引用操作符
操作数：一个数组名 + 一个索引值

int arr[10];//创建数组
 arr[9] = 10;//实用下标引用操作符。
 [ ]的两个操作数是arr和9。


 2. ( ) 函数调用操作符
接受一个或者多个操作数：第一个操作数是函数名，剩余的操作数就是传递给函数的参数。

#include <stdio.h>
 void test1()
 {
 printf("hehe\n");
 }
 void test2(const char *str)
 {
 printf("%s\n", str);
 }
 int main()
 {
 test1();            //实用（）作为函数调用操作符。
 test2("hello bit.");//实用（）作为函数调用操作符。
 return 0;
 }



 3. 访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名

#include <stdio.h>
struct Stu
{
 char name[10];
 int age;
 char sex[5];
 double score;
 }；
void set_age1(struct Stu stu)
{
 stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
 pStu->age = 18;//结构成员访问
}
int main()
{
 struct Stu stu;
 struct Stu* pStu = &stu;//结构成员访问

 stu.age = 20;//结构成员访问
 set_age1(stu);

 pStu->age = 20;//结构成员访问
 set_age2(pStu);
 return 0;
}
//实例1
char a,b,c;
...
a = b + c;

*/



//int main()
//{
//	int arr[10] = { 0 };
//	//
//	//arr[7] -->arr[0+7]--> *(arr+7) --> *(7+arr) --> 7[arr]
//	// 
//	//arr是数组首元素的地址
//	//arr+7就是跳过7个元素，就是arr[7]的地址，指向了第8个元素
//	//*(arr+7) 就是第8个元素（arr[7])
//	//
//	arr[7] = 8;
//	7[arr] = 9;
//
//
//	//[] arr 7
//	//3 + 5;
//	//5 + 3;
//	return 0;
//}




//函数定义
//int Add(int x, int y)
//{
//	return x + y;
//}
//
//int main()
//{
//	int a = 10;
//	int b = 20;
//	//函数调用
//	int c = Add(a, b);//() 就是函数调用操作符,操作数：Add,a,b
//	//
//	//sizeof a;
//
//	return 0;
//}



struct Stu           //这是一个类型名，类似于int 。sturct的作用是引出Stu     struct Stu 用来定义其他变量（写作为struct Stu s={0};） 
{
	char name[20];         //名字是name，容量是20
	int age;
	double score;
};


/*
//void set_stu(struct Stu s)
//{
//	s.age = 100;
//	s.score = 1000.0;
//	//s.name = "zhangsan";         //错误！name是数组名字，是一个地址，用strcpy
//	strcpy(s.name, "zhangsan");           //strcpy,拷贝字符串，把张三拷贝进去
//}

//void print_stu(struct Stu s)
//{
//	printf("%d %lf %s", s.age, s.score, s.name);
//
//}



错误！！！放进去的形参不是地址，不会影响实参！！！
打印的时候，形参的改变不影响实参！
所以应该传指针！！！

void set_stu(struct Stu *ps)       //*代表指针变量，ps是变量名
{
//别忘了括号
//(*ps).age=
//(*ps).score=
//strcpy((*ps).name,"zhangsan");


更简单的：
strcpy(ps->name,"zhangsan");             //ps->name 表示通过结构体指针ps找到他所对应的元素name
ps->age=20;
ps->score=100.0;
}





/*
*   箭头->操作符：指针找元素   结构体指针->成员          //但是，为了改变实参，传参仍然要传指针
*   点.操作符：(解引用的结构体指针名).成员
* 两者等价
* 
* 在使用的时候，无论是打印还是赋值，两者都要传指针
* /

*/
#include<string.h>
void set_stu(struct Stu* ps)
{
	//strcpy((*ps).name, "zhangsan");
	//(*ps).age = 20;
	//(*ps).score = 100.0;

	strcpy(ps->name, "zhangsan");
	ps->age = 20;
	ps->score = 100.0;
}

void print_stu(struct Stu* ps)
{
	printf("%s %d %lf\n", ps->name, ps->age, ps->score);//写成这种形式也可以，可以直接找到对应元素，只是这样写，会更浪费空间
}

int main()
{
	struct Stu s = { 0 }; 
	set_stu(&s);                     //s只是一个变量，不是数组！！！
	//set_stu(s);
	print_stu(&s);
	//print_stu(s);

	/*
	//print_stu(s);

	。。。。。。。
	printf(.......s.age,s.name......)
	使用箭头操作符，必须用指针！
	
	
	*/

	return 0;
}